﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;

namespace Core.Utility.Helper
{
    /// <summary>
    /// 文件助手列
    /// </summary>
    public static class FileHelper
    {

        /// <summary>
        /// 文件目录及路径检测
        /// </summary>
        /// <param name="fileFullPath">文件路径，若文件目录不存在，默认使用当前程序的根目录</param>
        /// <returns>目录及路径存在，未做任何操作返回true;新建目录或路径文件，返回false</returns>
        public static bool FileDirectoryPathExistsAndCreate(string fileFullPath)
        {
            bool bResult = true;
            string? strDirectoryName = Path.GetDirectoryName(fileFullPath);
            if (string.IsNullOrEmpty(strDirectoryName))
            {
                strDirectoryName = AppContext.BaseDirectory;
            }
            //判断路径文件夹是否存在
            if (!Directory.Exists(strDirectoryName))
            {
                Directory.CreateDirectory(strDirectoryName);
                bResult = false;
            }

            if (File.Exists(fileFullPath) == false)
            {
                using FileStream fs = File.Create(fileFullPath);
                fs.Close();
                bResult = false;
            }
            return bResult;
        }

        /// <summary>
        /// 输出文件，返回bool值
        /// </summary>
        /// <param name="strFileFullPath"></param>
        /// <param name="strContent"></param>
        /// <param name="encoding">默认值 参考:<see cref="Encoding.UTF8"/></param>
        /// <returns>成功返回true，失败返回false</returns>
        public static bool WriteFileReturnBool(string strFileFullPath, string? strContent, Encoding? encoding = null)
        {
            ReturnResultInfo<Exception> rri = WriteFile(strFileFullPath, strContent, encoding);
            return rri.Successed;
        }

        /// <summary>
        /// 输出文件
        /// </summary>
        /// <param name="strFileFullPath"></param>
        /// <param name="strContent"></param>
        /// <param name="encoding">默认值 参考:<see cref="Encoding.UTF8"/></param>
        /// <returns>成功 Successed 返回true，失败 Successed 返回false，Data为异常（如果有）</returns>
        public static ReturnResultInfo<Exception> WriteFile(string strFileFullPath, string? strContent, Encoding? encoding = null)
        {
            ReturnResultInfo<Exception> res = new ReturnResultInfo<Exception>();
            try
            {
                string? strDirectoryName = Path.GetDirectoryName(strFileFullPath);
                if (string.IsNullOrEmpty(strDirectoryName))
                {
                    strDirectoryName = AppContext.BaseDirectory;
                }
                //判断路径文件夹是否存在
                if (!Directory.Exists(strDirectoryName))
                {
                    Directory.CreateDirectory(strDirectoryName);
                }

                if (File.Exists(strFileFullPath) == false)
                {
                    using FileStream fs = File.Create(strFileFullPath);
                    {
                        fs.Close();
                    }
                }

                encoding ??= Encoding.UTF8;
                File.WriteAllText(strFileFullPath, strContent?.ToString(), encoding);
                res.Successed = true;
                res.Message = $"保存文件成功，文件已保存至{Environment.NewLine}{strFileFullPath}";
            }
            catch (Exception ex)
            {
                //throw new Exception("文件输出失败！" + ex.ToString());
                res.Successed = false;
                res.Message = $"保存文件失败，{Environment.NewLine}{ex.DetailMessage()}";
                res.Info = $"保存文件失败，{Environment.NewLine}{ex.DetailInfo()}";
            }
            return res;
        }

        /// <summary>
        /// 读取文件,返回字符串
        /// </summary>
        /// <param name="fileName">文件路径及名称</param>
        /// <param name="encoding">读取文件的编码,默认值UTF8</param>
        /// <returns>读取成功返回读取内容，读取失败返回 ""</returns>
        public static string? ReadFileToString(string fileName, Encoding? encoding = null)
        {
            ReturnResultInfo<string> res = ReadFile(fileName, encoding);
            return res.Data;
        }


        /// <summary>
        /// 读取文件,返回字符串
        /// </summary>
        /// <param name="fileName">文件路径及名称</param>
        /// <param name="encoding">读取文件的编码,默认值UTF8</param>
        /// <returns>Successd=true表示读取成功，Successed = false表示读取失败，Data为读取内容</returns>
        public static ReturnResultInfo<string> ReadFile(string fileName, Encoding? encoding = null)
        {
            ReturnResultInfo<string> res = new ReturnResultInfo<string>();
            res.Data = "";
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }

            if (!File.Exists(fileName))
            {
                res.Message = $"文件：{fileName}不存在！";
                return res;
            }

            try
            {
                FileStream fs = new(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                StreamReader m_streamReader = new StreamReader(fs, encoding);
                StringBuilder sbContent = new StringBuilder();
                try
                {
                    //使用StreamReader类来读取文件
                    m_streamReader.BaseStream.Seek(0, SeekOrigin.Begin);
                    //从数据流中读取每一行，直到文件的最后一行，并在textBox中显示出内容，其中textBox为文本框，如果不用可以改为别的                
                    string? strLine = m_streamReader.ReadLine();
                    while (strLine != null)
                    {
                        sbContent.AppendLine(strLine);
                        strLine = m_streamReader.ReadLine();
                    }
                    res.Data = sbContent.ToString();
                    res.Message = $"操作成功！";
                    res.Successed = true;
                }
                catch (Exception ex)
                {
                    res.Message = $"读取文件出错！{Environment.NewLine}{ex.DetailMessage()}{Environment.NewLine}文件路径：{Environment.NewLine}{fileName}";
                }
                finally
                {
                    //关闭此StreamReader对象
                    m_streamReader.Close();
                    fs.Close();
                }
            }
            catch (Exception e)
            {
                res.Message = $"读取文件出错！{Environment.NewLine}{e.DetailMessage()}{Environment.NewLine}文件路径：{Environment.NewLine}{fileName}";
            }
            return res;
        }

        /// <summary>
        /// 获取指定目录中的匹配项（文件或目录）
        /// </summary>
        /// <param name="dir">要搜索的目录</param>
        /// <param name="regexPattern">项名模式（正则）。null表示忽略模式匹配，返回所有项</param>
        /// <param name="recurse">是否搜索子目录</param>
        /// <param name="throwEx">是否抛异常</param>
        /// <returns></returns>
        public static List<string> GetFileSystemEntries(string dir, string? regexPattern = null, bool recurse = false,
            bool throwEx = false)
        {
            List<string> list = new List<string>();

            try
            {
                foreach (string item in Directory.GetFileSystemEntries(dir))
                {
                    try
                    {
                        if (regexPattern == null || Regex.IsMatch(Path.GetFileName(item), regexPattern,
                            RegexOptions.IgnoreCase | RegexOptions.Multiline))
                        {
                            list.Add(item);
                        }

                        //递归
                        if (recurse && (File.GetAttributes(item) & FileAttributes.Directory) ==
                            FileAttributes.Directory)
                        {
                            list.AddRange(GetFileSystemEntries(item, regexPattern, true));
                        }
                    }
                    catch
                    {
                        if (throwEx)
                        {
                            throw;
                        }
                    }
                }
            }
            catch
            {
                if (throwEx)
                {
                    throw;
                }
            }

            return list;
        }

        /// <summary>
        /// 获取指定目录中的匹配文件
        /// </summary>
        /// <param name="dir">要搜索的目录</param>
        /// <param name="regexPattern">文件名模式（正则）。null表示忽略模式匹配，返回所有文件</param>
        /// <param name="searchChildDirectory">是否搜索子目录</param>
        /// <param name="regexOptions">通配符表达式选项，参考：<see cref="RegexOptions"/></param>
        /// <param name="throwEx">是否抛异常</param>
        /// <returns></returns>
        public static List<string> GetFiles(
            string dir, 
            string? regexPattern = null, 
            bool searchChildDirectory = false, 
            RegexOptions regexOptions = RegexOptions.IgnoreCase | RegexOptions.Multiline,
            bool throwEx = false)
        {
            List<string> list = new List<string>();

            try
            {
                foreach (string item in Directory.GetFileSystemEntries(dir))
                {
                    try
                    {
                        bool isFile = (File.GetAttributes(item) & FileAttributes.Directory) != FileAttributes.Directory;

                        if (isFile && (regexPattern == null
                                       || Regex.IsMatch(Path.GetFileName(item), regexPattern, regexOptions)))
                        {
                            list.Add(item);
                        }

                        //递归
                        if (searchChildDirectory && !isFile)
                        {
                            list.AddRange(GetFiles(item, regexPattern, true,regexOptions));
                        }
                    }
                    catch
                    {
                        if (throwEx)
                        {
                            throw;
                        }
                    }
                }
            }
            catch
            {
                if (throwEx)
                {
                    throw;
                }
            }

            return list;
        }

        /// <summary>
        /// 获取指定目录中的匹配目录
        /// </summary>
        /// <param name="dir">要搜索的目录</param>
        /// <param name="regexPattern">目录名模式（正则）。null表示忽略模式匹配，返回所有目录</param>
        /// <param name="recurse">是否搜索子目录</param>
        /// <param name="regexOptions">通配符表达式选项，参考：<see cref="RegexOptions"/></param>
        /// <param name="throwEx">是否抛异常</param>
        /// <returns></returns>
        public static List<string> GetDirectories(
            string dir, 
            string? regexPattern = null, 
            bool recurse = false,
            RegexOptions regexOptions = RegexOptions.IgnoreCase | RegexOptions.Multiline,
            bool throwEx = false)
        {
            List<string> list = new List<string>();

            try
            {
                foreach (string item in Directory.GetDirectories(dir))
                {
                    try
                    {
                        if (regexPattern == null || Regex.IsMatch(Path.GetFileName(item), regexPattern, regexOptions))
                        {
                            list.Add(item);
                        }

                        //递归
                        if (recurse)
                        {
                            list.AddRange(GetDirectories(item, regexPattern, true,regexOptions));
                        }
                    }
                    catch
                    {
                        if (throwEx)
                        {
                            throw;
                        }
                    }
                }
            }
            catch
            {
                if (throwEx)
                {
                    throw;
                }
            }

            return list;
        }

        /// <summary>
        /// 指定目录全路径，使用通配符查找文件路径
        /// </summary>
        /// <param name="directoryFullPath">目录的全路径，若目录不存在，返回 new List&lt;string&gt;()</param>
        /// <param name="filePathOrNameRegx">文件路径或名称通配表达式，例:\data\* 或 \data\*.dll </param>
        /// <param name="regexOptions">表达式选项，参考：<see cref="RegexOptions"/></param>
        /// <param name="isRecursive">是否递归查找</param>
        /// <returns>返回值是匹配文件的全路径，包含盘符、目录、文件名、文件扩展名等</returns>
        public static List<string> WildcardFindFileFullPath(
            string directoryFullPath,
            string filePathOrNameRegx,
            bool isRecursive = false, 
            RegexOptions regexOptions = RegexOptions.IgnoreCase | RegexOptions.Multiline)
        {
            List<string> res = new List<string>();         
            string regxString = RegexHelper.WildCardToRegex(filePathOrNameRegx);
            if(Directory.Exists(directoryFullPath))
            {
                res = GetFiles(directoryFullPath, regxString, isRecursive,regexOptions);
            }
            return res;
        }

        /// <summary>
        /// 获取指定文件后缀名的正则规则字符串
        /// </summary>
        /// <param name="extensionNames">后缀名List，后缀名前不需要包含.</param>
        /// <returns></returns>
        public static string GetAssignExtensionNameRegexRules(List<string> extensionNames)
        {
            for (int i = 0; i < extensionNames.Count; i++)
            {
                string extensionName = extensionNames[i];
                extensionNames[i] = @"^^[^\.].*\." + extensionName + @"$";
            }

            return string.Join("|", extensionNames.ToArray());
        }

        /// <summary>
        /// 获取文件编码
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static Encoding GetEncoding(string filename)
        {
            // Read the BOM
            var bom = new byte[4];
            using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
            {
                file.Read(bom, 0, 4);
            }

            // Analyze the BOM
            if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76)
#pragma warning disable SYSLIB0001 // 类型或成员已过时
                return Encoding.UTF7;
#pragma warning restore SYSLIB0001 // 类型或成员已过时

            if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
                return Encoding.UTF8;

            if (bom[0] == 0xff && bom[1] == 0xfe)
                return Encoding.Unicode; //UTF-16LE

            if (bom[0] == 0xfe && bom[1] == 0xff)
                return Encoding.BigEndianUnicode; //UTF-16BE

            if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff)
                return Encoding.UTF32;

            return Encoding.Default;
        }
    }
}
